{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Advanced MVO - custom objectives\n",
    "\n",
    "PyPortfolioOpt has implemented some of the most common objective functions (e.g `min_volatility`, `max_sharpe`, `max_quadratic_utility`, `efficient_risk`, `efficient_return`). However, sometimes yoy may have an idea for a different objective function.\n",
    "\n",
    "In this cookbook recipe, we cover:\n",
    "\n",
    "- Mininimising transaction costs\n",
    "- Custom convex objectives\n",
    "- Custom nonconvex objectives\n",
    "\n",
    "## Acquiring data\n",
    "\n",
    "As discussed in the previous notebook, assets are an exogenous input (i.e you must come up with a list of tickers). We will use `yfinance` to download data for thesee tickers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import yfinance as yf\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "tickers = [\"BLK\", \"BAC\", \"AAPL\", \"TM\", \"WMT\",\n",
    "           \"JD\", \"INTU\", \"MA\", \"UL\", \"CVS\",\n",
    "           \"DIS\", \"AMD\", \"NVDA\", \"PBI\", \"TGT\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[*********************100%***********************]  15 of 15 completed\n"
     ]
    }
   ],
   "source": [
    "ohlc = yf.download(tickers, period=\"max\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>AAPL</th>\n",
       "      <th>AMD</th>\n",
       "      <th>BAC</th>\n",
       "      <th>BLK</th>\n",
       "      <th>CVS</th>\n",
       "      <th>DIS</th>\n",
       "      <th>INTU</th>\n",
       "      <th>JD</th>\n",
       "      <th>MA</th>\n",
       "      <th>NVDA</th>\n",
       "      <th>PBI</th>\n",
       "      <th>TGT</th>\n",
       "      <th>TM</th>\n",
       "      <th>UL</th>\n",
       "      <th>WMT</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Date</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>2021-01-22</th>\n",
       "      <td>139.070007</td>\n",
       "      <td>92.790001</td>\n",
       "      <td>31.549999</td>\n",
       "      <td>735.049988</td>\n",
       "      <td>74.120003</td>\n",
       "      <td>172.779999</td>\n",
       "      <td>374.850006</td>\n",
       "      <td>94.910004</td>\n",
       "      <td>328.989990</td>\n",
       "      <td>548.500000</td>\n",
       "      <td>7.19</td>\n",
       "      <td>191.910004</td>\n",
       "      <td>147.949997</td>\n",
       "      <td>59.660000</td>\n",
       "      <td>146.330002</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2021-01-25</th>\n",
       "      <td>142.919998</td>\n",
       "      <td>94.129997</td>\n",
       "      <td>31.160000</td>\n",
       "      <td>722.979980</td>\n",
       "      <td>73.209999</td>\n",
       "      <td>171.889999</td>\n",
       "      <td>374.429993</td>\n",
       "      <td>98.379997</td>\n",
       "      <td>329.190002</td>\n",
       "      <td>546.130005</td>\n",
       "      <td>7.53</td>\n",
       "      <td>190.149994</td>\n",
       "      <td>146.720001</td>\n",
       "      <td>60.970001</td>\n",
       "      <td>146.199997</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2021-01-26</th>\n",
       "      <td>143.160004</td>\n",
       "      <td>94.709999</td>\n",
       "      <td>30.940001</td>\n",
       "      <td>721.849976</td>\n",
       "      <td>74.230003</td>\n",
       "      <td>169.559998</td>\n",
       "      <td>374.799988</td>\n",
       "      <td>96.970001</td>\n",
       "      <td>327.700012</td>\n",
       "      <td>537.409973</td>\n",
       "      <td>13.63</td>\n",
       "      <td>187.699997</td>\n",
       "      <td>145.979996</td>\n",
       "      <td>60.900002</td>\n",
       "      <td>147.509995</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2021-01-27</th>\n",
       "      <td>142.059998</td>\n",
       "      <td>88.839996</td>\n",
       "      <td>29.830000</td>\n",
       "      <td>697.789978</td>\n",
       "      <td>72.419998</td>\n",
       "      <td>163.029999</td>\n",
       "      <td>363.109985</td>\n",
       "      <td>90.089996</td>\n",
       "      <td>315.489990</td>\n",
       "      <td>516.710022</td>\n",
       "      <td>10.81</td>\n",
       "      <td>178.279999</td>\n",
       "      <td>142.639999</td>\n",
       "      <td>59.340000</td>\n",
       "      <td>143.839996</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2021-01-28</th>\n",
       "      <td>137.089996</td>\n",
       "      <td>87.519997</td>\n",
       "      <td>30.320000</td>\n",
       "      <td>719.909973</td>\n",
       "      <td>72.610001</td>\n",
       "      <td>171.880005</td>\n",
       "      <td>369.950012</td>\n",
       "      <td>91.410004</td>\n",
       "      <td>324.279999</td>\n",
       "      <td>522.039978</td>\n",
       "      <td>9.27</td>\n",
       "      <td>183.580002</td>\n",
       "      <td>143.300003</td>\n",
       "      <td>59.080002</td>\n",
       "      <td>143.750000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                  AAPL        AMD        BAC         BLK        CVS  \\\n",
       "Date                                                                  \n",
       "2021-01-22  139.070007  92.790001  31.549999  735.049988  74.120003   \n",
       "2021-01-25  142.919998  94.129997  31.160000  722.979980  73.209999   \n",
       "2021-01-26  143.160004  94.709999  30.940001  721.849976  74.230003   \n",
       "2021-01-27  142.059998  88.839996  29.830000  697.789978  72.419998   \n",
       "2021-01-28  137.089996  87.519997  30.320000  719.909973  72.610001   \n",
       "\n",
       "                   DIS        INTU         JD          MA        NVDA    PBI  \\\n",
       "Date                                                                           \n",
       "2021-01-22  172.779999  374.850006  94.910004  328.989990  548.500000   7.19   \n",
       "2021-01-25  171.889999  374.429993  98.379997  329.190002  546.130005   7.53   \n",
       "2021-01-26  169.559998  374.799988  96.970001  327.700012  537.409973  13.63   \n",
       "2021-01-27  163.029999  363.109985  90.089996  315.489990  516.710022  10.81   \n",
       "2021-01-28  171.880005  369.950012  91.410004  324.279999  522.039978   9.27   \n",
       "\n",
       "                   TGT          TM         UL         WMT  \n",
       "Date                                                       \n",
       "2021-01-22  191.910004  147.949997  59.660000  146.330002  \n",
       "2021-01-25  190.149994  146.720001  60.970001  146.199997  \n",
       "2021-01-26  187.699997  145.979996  60.900002  147.509995  \n",
       "2021-01-27  178.279999  142.639999  59.340000  143.839996  \n",
       "2021-01-28  183.580002  143.300003  59.080002  143.750000  "
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prices = ohlc[\"Adj Close\"]\n",
    "prices.tail()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Expected returns and risk models\n",
    "\n",
    "In this notebook, we will use James-Stein shrinkage and semicovariance (which only penalises downside risk)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'1.3.1'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pypfopt\n",
    "pypfopt.__version__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pypfopt import risk_models, expected_returns\n",
    "from pypfopt import plotting\n",
    "\n",
    "mu = expected_returns.capm_return(prices)\n",
    "S = risk_models.semicovariance(prices)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x360 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "mu.plot.barh(figsize=(10,5));"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plotting.plot_covariance(S, plot_correlation=True);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Min volatility with a transaction cost objective\n",
    "\n",
    "Let's say that you already have a portfolio, and want to now optimize it. It could be quite expensive to completely reallocate, so you may want to take into account transaction costs. PyPortfolioOpt provides a simple objective to account for this.\n",
    "\n",
    "Note: this objective will not play nicely with `max_sharpe`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Pretend that you started with a default-weight allocation\n",
    "initial_weights = np.array([1/len(tickers)] * len(tickers))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('AAPL', 0.02233),\n",
       "             ('AMD', 0.0),\n",
       "             ('BAC', 0.00295),\n",
       "             ('BLK', 0.06667),\n",
       "             ('CVS', 0.06667),\n",
       "             ('DIS', 0.06667),\n",
       "             ('INTU', 0.06667),\n",
       "             ('JD', 0.24971),\n",
       "             ('MA', 0.06667),\n",
       "             ('NVDA', 0.05834),\n",
       "             ('PBI', 0.06667),\n",
       "             ('TGT', 0.06667),\n",
       "             ('TM', 0.06667),\n",
       "             ('UL', 0.06667),\n",
       "             ('WMT', 0.06667)])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from pypfopt import EfficientFrontier, objective_functions\n",
    "\n",
    "ef = EfficientFrontier(mu, S)\n",
    "\n",
    "# 1% broker commission\n",
    "ef.add_objective(objective_functions.transaction_cost, w_prev=initial_weights, k=0.01)\n",
    "ef.min_volatility()\n",
    "weights = ef.clean_weights()\n",
    "weights"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice that many of the weights are 0.06667, i.e your original equal weight. In fact, the only change has been an allocation of AMD's weight to JD. If we lower the cost `k`, the allocation will change more:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('AAPL', 0.0),\n",
       "             ('AMD', 0.0),\n",
       "             ('BAC', 0.0),\n",
       "             ('BLK', 0.06667),\n",
       "             ('CVS', 0.03844),\n",
       "             ('DIS', 0.0),\n",
       "             ('INTU', 0.01533),\n",
       "             ('JD', 0.48839),\n",
       "             ('MA', 0.16953),\n",
       "             ('NVDA', 0.0),\n",
       "             ('PBI', 0.0),\n",
       "             ('TGT', 0.00077),\n",
       "             ('TM', 0.06667),\n",
       "             ('UL', 0.08754),\n",
       "             ('WMT', 0.06667)])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ef = EfficientFrontier(mu, S)\n",
    "ef.add_objective(objective_functions.transaction_cost, w_prev=initial_weights, k=0.001)\n",
    "ef.min_volatility()\n",
    "weights = ef.clean_weights()\n",
    "weights"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The optimizer seems to really like JD. The reason for this is that it is highly anticorrelated to other assets (notice the dark column in the covariance plot). Hence, historically, it adds a lot of diversification. But it is dangerous to place too much emphasis on what happened in the past, so we may want to limit the asset weights. \n",
    "\n",
    "In addition, we notice that 4 stocks have now been allocated zero weight, which may be undesirable. Both of these problems can be fixed by adding an [L2 regularisation objective](https://pyportfolioopt.readthedocs.io/en/latest/EfficientFrontier.html#more-on-l2-regularisation). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('AAPL', 0.06179),\n",
       "             ('AMD', 0.05379),\n",
       "             ('BAC', 0.06202),\n",
       "             ('BLK', 0.07042),\n",
       "             ('CVS', 0.06667),\n",
       "             ('DIS', 0.06506),\n",
       "             ('INTU', 0.06667),\n",
       "             ('JD', 0.0775),\n",
       "             ('MA', 0.0734),\n",
       "             ('NVDA', 0.06399),\n",
       "             ('PBI', 0.06508),\n",
       "             ('TGT', 0.06591),\n",
       "             ('TM', 0.06909),\n",
       "             ('UL', 0.07073),\n",
       "             ('WMT', 0.0679)])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ef = EfficientFrontier(mu, S)\n",
    "ef.add_objective(objective_functions.transaction_cost, w_prev=initial_weights, k=0.001)\n",
    "ef.add_objective(objective_functions.L2_reg)\n",
    "ef.min_volatility()\n",
    "weights = ef.clean_weights()\n",
    "weights"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This has had too much of an evening-out effect. After all, if the resulting allocation is going to be so close to equal weights, we may as well stick with our initial allocation. We can reduce the strength of the L2 regularisation by reducing `gamma`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('AAPL', 0.01915),\n",
       "             ('AMD', 0.0),\n",
       "             ('BAC', 0.00684),\n",
       "             ('BLK', 0.09548),\n",
       "             ('CVS', 0.06667),\n",
       "             ('DIS', 0.04251),\n",
       "             ('INTU', 0.06071),\n",
       "             ('JD', 0.19403),\n",
       "             ('MA', 0.13693),\n",
       "             ('NVDA', 0.0299),\n",
       "             ('PBI', 0.04005),\n",
       "             ('TGT', 0.05035),\n",
       "             ('TM', 0.08321),\n",
       "             ('UL', 0.10267),\n",
       "             ('WMT', 0.07152)])"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ef = EfficientFrontier(mu, S)\n",
    "ef.add_objective(objective_functions.transaction_cost, w_prev=initial_weights, k=0.001)\n",
    "ef.add_objective(objective_functions.L2_reg, gamma=0.05)  # default is 1\n",
    "ef.min_volatility()\n",
    "weights = ef.clean_weights()\n",
    "weights"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Expected annual return: 18.0%\n",
      "Annual volatility: 9.4%\n",
      "Sharpe Ratio: 1.69\n"
     ]
    }
   ],
   "source": [
    "ef.portfolio_performance(verbose=True);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This portfolio is now reasonably balanced, but also puts significantly more weight on JD. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 720x720 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "pd.Series(weights).plot.pie(figsize=(10,10));"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Custom convex objectives\n",
    "\n",
    "PyPortfolioOpt comes with the following built-in objective functions, as of v1.2.1:\n",
    "\n",
    "- Portfolio variance (i.e square of volatility)\n",
    "- Portfolio return\n",
    "- Sharpe ratio\n",
    "- L2 regularisation (minimising this reduces nonzero weights)\n",
    "- Quadratic utility\n",
    "- Transaction cost model (a simple one)\n",
    "\n",
    "However, you may want have a different objective. If this new objective is **convex**, you can optimize a portfolio with the full benefit of PyPortfolioOpt's modular syntax, for example adding other constraints and objectives.\n",
    "\n",
    "To demonstrate this, we will minimise the **logarithmic-barrier** function suggested in the paper 60 Years of Portfolio Optimization, by Kolm et al (2014):\n",
    "\n",
    "$$f(w, S, k) = w^T S w - k \\sum_{i=1}^N \\ln w$$\n",
    "\n",
    "We must first convert this mathematical objective into the language of cvxpy. Cvxpy is a powerful modelling language for convex optimization problems. It is clean and easy to use, the only caveat is that objectives must be expressed with `cvxpy` functions, a list of which can be found [here](https://www.cvxpy.org/tutorial/functions/index.html)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "import cvxpy as cp\n",
    "\n",
    "# Note: functions are minimised. If you want to maximise an objective, stick a minus sign in it.\n",
    "def logarithmic_barrier_objective(w, cov_matrix, k=0.1):\n",
    "    log_sum = cp.sum(cp.log(w))\n",
    "    var = cp.quad_form(w, cov_matrix)\n",
    "    return var - k * log_sum"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once we have written the objective function, we can just use the `ef.convex_objective()` to minimise the objective."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('AAPL', 0.03824),\n",
       "             ('AMD', 0.02618),\n",
       "             ('BAC', 0.03724),\n",
       "             ('BLK', 0.07674),\n",
       "             ('CVS', 0.05578),\n",
       "             ('DIS', 0.04616),\n",
       "             ('INTU', 0.05245),\n",
       "             ('JD', 0.2),\n",
       "             ('MA', 0.11598),\n",
       "             ('NVDA', 0.04185),\n",
       "             ('PBI', 0.04538),\n",
       "             ('TGT', 0.04891),\n",
       "             ('TM', 0.06917),\n",
       "             ('UL', 0.08254),\n",
       "             ('WMT', 0.06337)])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ef = EfficientFrontier(mu, S, weight_bounds=(0.01, 0.2))\n",
    "ef.convex_objective(logarithmic_barrier_objective, cov_matrix=S, k=0.001)\n",
    "weights = ef.clean_weights()\n",
    "weights"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Expected annual return: 18.7%\n",
      "Annual volatility: 10.1%\n",
      "Sharpe Ratio: 1.66\n"
     ]
    }
   ],
   "source": [
    "ef.portfolio_performance(verbose=True);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is compatible with all the constraints discussed in the previous recipe. Let's say that we want to limit JD's weight to 15%."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('AAPL', 0.03914),\n",
       "             ('AMD', 0.0264),\n",
       "             ('BAC', 0.0378),\n",
       "             ('BLK', 0.08259),\n",
       "             ('CVS', 0.05832),\n",
       "             ('DIS', 0.04759),\n",
       "             ('INTU', 0.05474),\n",
       "             ('JD', 0.15),\n",
       "             ('MA', 0.13295),\n",
       "             ('NVDA', 0.0432),\n",
       "             ('PBI', 0.04705),\n",
       "             ('TGT', 0.05057),\n",
       "             ('TM', 0.07351),\n",
       "             ('UL', 0.08928),\n",
       "             ('WMT', 0.06684)])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ef = EfficientFrontier(mu, S, weight_bounds=(0.01, 0.2))\n",
    "jd_index = ef.tickers.index(\"JD\")  # get the index of JD\n",
    "ef.add_constraint(lambda w: w[jd_index] <= 0.15)\n",
    "ef.convex_objective(logarithmic_barrier_objective, cov_matrix=S, k=0.001)\n",
    "weights = ef.clean_weights()\n",
    "weights"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Custom nonconvex objectives\n",
    "\n",
    "In some cases, you may be trying to optimize for nonconvex objectives. Optimization in general is a very hard problem, so please be aware that you may have mixed results in that case. Convex problems, on the other hand, are well understood and can be solved with nice theoretical guarantees.\n",
    "\n",
    "PyPortfolioOpt does offer some functionality for nonconvex optimization, but it is not really encouraged. In particular, nonconvex optimization is not compatible with PyPortfolioOpt's modular constraints API.\n",
    "\n",
    "As an example, we will use the Deviation Risk Parity objective from Kolm et al (2014). Because we are not using a convex solver, we don't have to define it using `cvxpy` functions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "def deviation_risk_parity(w, cov_matrix):\n",
    "    diff = w * np.dot(cov_matrix, w) - (w * np.dot(cov_matrix, w)).reshape(-1, 1)\n",
    "    return (diff ** 2).sum().sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('AAPL', 0.05548),\n",
       "             ('AMD', 0.02632),\n",
       "             ('BAC', 0.05766),\n",
       "             ('BLK', 0.07681),\n",
       "             ('CVS', 0.07115),\n",
       "             ('DIS', 0.06542),\n",
       "             ('INTU', 0.06962),\n",
       "             ('JD', 0.07815),\n",
       "             ('MA', 0.0788),\n",
       "             ('NVDA', 0.06256),\n",
       "             ('PBI', 0.06566),\n",
       "             ('TGT', 0.06743),\n",
       "             ('TM', 0.07492),\n",
       "             ('UL', 0.07692),\n",
       "             ('WMT', 0.0731)])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ef = EfficientFrontier(mu, S, weight_bounds=(0.01, 0.12))\n",
    "ef.nonconvex_objective(deviation_risk_parity, ef.cov_matrix)\n",
    "weights = ef.clean_weights()\n",
    "weights"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, let's say we now want to enforce that JD has a weight of 10%. In the convex case, this would be as simple as:\n",
    "\n",
    "```python\n",
    "ef.add_objective(lambda w: w[jd_index] == 0.10)\n",
    "```\n",
    "\n",
    "But unfortunately, scipy does not allow for such intuitive syntax. You will need to rearrange your constraints to make them either `=0` or `<= 0`. \n",
    "\n",
    "```python\n",
    "constraints = [\n",
    "    # First constraint\n",
    "     {\"type\": \"eq\",  # equality constraint,\n",
    "      \"fun\": lambda w: w[1] - 0.2},  # the equality functions are assumed to = 0 \n",
    "      \n",
    "    # Second constraint\n",
    "    {\"type\": \"ineq\",  # inequality constraint\n",
    "     \"fun\": lambda w: w[0] - 0.5}   # inequality functions <= 0\n",
    "]\n",
    "```\n",
    "\n",
    "For more information, you can consult the [scipy docs](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html), but they aren't very helpful."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('AAPL', 0.05542),\n",
       "             ('AMD', 0.0298),\n",
       "             ('BAC', 0.05735),\n",
       "             ('BLK', 0.07367),\n",
       "             ('CVS', 0.06894),\n",
       "             ('DIS', 0.06406),\n",
       "             ('INTU', 0.0676),\n",
       "             ('JD', 0.1),\n",
       "             ('MA', 0.07529),\n",
       "             ('NVDA', 0.06145),\n",
       "             ('PBI', 0.06419),\n",
       "             ('TGT', 0.06579),\n",
       "             ('TM', 0.0721),\n",
       "             ('UL', 0.07376),\n",
       "             ('WMT', 0.07059)])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ef = EfficientFrontier(mu, S, weight_bounds=(0.01, 0.12))\n",
    "\n",
    "ef.nonconvex_objective(\n",
    "    deviation_risk_parity,\n",
    "    objective_args=S,\n",
    "    weights_sum_to_one=True,\n",
    "    constraints=[\n",
    "        {\"type\": \"eq\", \"fun\": lambda w: w[jd_index] - 0.10},  \n",
    "    ],\n",
    ")\n",
    "\n",
    "weights = ef.clean_weights()\n",
    "weights"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## More examples of nonconvex objectives\n",
    "\n",
    "The scipy format is not intuitive and is hard to explain, so here are a bunch of examples (adapted from the tests). Some of these are actually convex, so you should use `convex_objective` instead. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('AAPL', 0.04324),\n",
       "             ('AMD', 0.03142),\n",
       "             ('BAC', 0.04275),\n",
       "             ('BLK', 0.07606),\n",
       "             ('CVS', 0.05896),\n",
       "             ('DIS', 0.05053),\n",
       "             ('INTU', 0.0562),\n",
       "             ('JD', 0.1765),\n",
       "             ('MA', 0.1006),\n",
       "             ('NVDA', 0.04714),\n",
       "             ('PBI', 0.05017),\n",
       "             ('TGT', 0.05296),\n",
       "             ('TM', 0.06948),\n",
       "             ('UL', 0.07936),\n",
       "             ('WMT', 0.06461)])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Another example of deviation risk parity\n",
    "def deviation_risk_parity(w, cov_matrix):\n",
    "    n = cov_matrix.shape[0]\n",
    "    rp = (w * (cov_matrix @ w)) / cp.quad_form(w, cov_matrix)\n",
    "    return cp.sum_squares(rp - 1 / n).value\n",
    "\n",
    "ef = EfficientFrontier(mu, S)\n",
    "ef.nonconvex_objective(deviation_risk_parity, ef.cov_matrix)\n",
    "weights = ef.clean_weights()\n",
    "weights"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('AAPL', 0.1),\n",
       "             ('AMD', 0.03391),\n",
       "             ('BAC', 0.0477),\n",
       "             ('BLK', 0.07674),\n",
       "             ('CVS', 0.06093),\n",
       "             ('DIS', 0.05398),\n",
       "             ('INTU', 0.05745),\n",
       "             ('JD', 0.10687),\n",
       "             ('MA', 0.09381),\n",
       "             ('NVDA', 0.05099),\n",
       "             ('PBI', 0.05455),\n",
       "             ('TGT', 0.05561),\n",
       "             ('TM', 0.06824),\n",
       "             ('UL', 0.07546),\n",
       "             ('WMT', 0.06377)])"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Deviation risk parity with weight bound on the first asset\n",
    "ef = EfficientFrontier(mu, S)\n",
    "ef.nonconvex_objective(deviation_risk_parity, \n",
    "                       ef.cov_matrix, \n",
    "                       constraints=[{\"type\":\"eq\", \"fun\":lambda w: w[0] - 0.1}])\n",
    "weights = ef.clean_weights()\n",
    "weights"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('AAPL', 0.09548),\n",
       "             ('AMD', 0.16658),\n",
       "             ('BAC', 0.00681),\n",
       "             ('BLK', -0.12506),\n",
       "             ('CVS', -0.17303),\n",
       "             ('DIS', 0.21509),\n",
       "             ('INTU', 0.1153),\n",
       "             ('JD', 0.16446),\n",
       "             ('MA', 0.65232),\n",
       "             ('NVDA', 0.41921),\n",
       "             ('PBI', -0.04438),\n",
       "             ('TGT', 0.00676),\n",
       "             ('TM', -0.48244),\n",
       "             ('UL', -0.86122),\n",
       "             ('WMT', -0.15589)])"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Market-neutral efficient risk.\n",
    "# Please use ef.efficient_risk() for anything serious.\n",
    "target_risk = 0.19\n",
    "ef = EfficientFrontier(mu, S, weight_bounds=(None, None))\n",
    "\n",
    "# Weights sum to zero\n",
    "weight_constr = {\"type\": \"eq\", \"fun\": lambda w: np.sum(w)}\n",
    "\n",
    "# Portfolio vol less than target vol\n",
    "risk_constr = {\n",
    "    \"type\": \"eq\",\n",
    "    \"fun\": lambda w: target_risk ** 2 - np.dot(w.T, np.dot(ef.cov_matrix, w)),\n",
    "}\n",
    "constraints = [weight_constr, risk_constr]\n",
    "\n",
    "ef.nonconvex_objective(\n",
    "    lambda w, mu: -w.T.dot(mu),  # min negative return i.e max return\n",
    "    objective_args=(ef.expected_returns),\n",
    "    weights_sum_to_one=False,\n",
    "    constraints=constraints,\n",
    ")\n",
    "weights = ef.clean_weights()\n",
    "weights"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('AAPL', 0.0),\n",
       "             ('AMD', 0.04621),\n",
       "             ('BAC', 0.0),\n",
       "             ('BLK', 0.0),\n",
       "             ('CVS', 0.0),\n",
       "             ('DIS', 0.0),\n",
       "             ('INTU', 0.0),\n",
       "             ('JD', 0.0),\n",
       "             ('MA', 0.0),\n",
       "             ('NVDA', 0.95379),\n",
       "             ('PBI', 0.0),\n",
       "             ('TGT', 0.0),\n",
       "             ('TM', 0.0),\n",
       "             ('UL', 0.0),\n",
       "             ('WMT', 0.0)])"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Utility objective - you could actually use ef.max_quadratic_utility\n",
    "ef = EfficientFrontier(mu, S)\n",
    "\n",
    "def utility_obj(weights, mu, cov_matrix, k=1):\n",
    "    return -weights.dot(mu) + k * np.dot(weights.T, np.dot(cov_matrix, weights))\n",
    "\n",
    "ef.nonconvex_objective(\n",
    "    utility_obj,\n",
    "    objective_args=(ef.expected_returns, ef.cov_matrix, 1)\n",
    "    # default is for weights to sum to 1\n",
    ")\n",
    "\n",
    "weights = ef.clean_weights()\n",
    "weights"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.0000000000000002"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ef.weights.sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "OrderedDict([('AAPL', 0.06667),\n",
       "             ('AMD', 0.06667),\n",
       "             ('BAC', 0.06667),\n",
       "             ('BLK', 0.06667),\n",
       "             ('CVS', 0.06667),\n",
       "             ('DIS', 0.06667),\n",
       "             ('INTU', 0.06667),\n",
       "             ('JD', 0.06667),\n",
       "             ('MA', 0.06667),\n",
       "             ('NVDA', 0.06667),\n",
       "             ('PBI', 0.06667),\n",
       "             ('TGT', 0.06667),\n",
       "             ('TM', 0.06667),\n",
       "             ('UL', 0.06667),\n",
       "             ('WMT', 0.06667)])"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Kelly objective with weight bounds on zeroth asset\n",
    "def kelly_objective(w, e_returns, cov_matrix, k=3):\n",
    "    variance = np.dot(w.T, np.dot(cov_matrix, w))\n",
    "    objective = variance * 0.5 * k - np.dot(w, e_returns)\n",
    "    return objective\n",
    "\n",
    "lower_bounds, upper_bounds = 0.01, 0.3\n",
    "ef = EfficientFrontier(mu, S)\n",
    "ef.nonconvex_objective(\n",
    "    kelly_objective,\n",
    "    objective_args=(ef.expected_returns, ef.cov_matrix, 1000),\n",
    "    constraints=[\n",
    "        {\"type\": \"eq\", \"fun\": lambda w: np.sum(w) - 1},\n",
    "        {\"type\": \"ineq\", \"fun\": lambda w: w[0] - lower_bounds},\n",
    "        {\"type\": \"ineq\", \"fun\": lambda w: upper_bounds - w[0]},\n",
    "    ],\n",
    ")\n",
    "\n",
    "weights = ef.clean_weights()\n",
    "weights"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pyportfolioopt",
   "language": "python",
   "name": "pyportfolioopt"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
